﻿#region Using Statement
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using BreakOutBattles.Game.Physics;
using BreakOutBattles.Sound;
#endregion

namespace BreakOutBattles.Game.Items
{
    #region Enums
    public enum PaddleDirection
    {
        LEFT,
        RIGHT,
        IDLE
    }
    #endregion

    /// <summary>
    /// The paddle is a player controled game component that the player uses to
    /// keep a ball within the playable area of the screen. A Paddle object is 
    /// able to be controlled in two different ways, localy and networked depending on the type of the player.
    /// </summary>
    public class Paddle : GameItem, ISolidObject
    {
        #region Fields
        private const int LEFT_BOUND_MARGIN = 2;
        private const int RIGHT_BOUND_MARGIN = 20;
        private const int MOVING_SPEED = 400;

        private Player player;
        private Rectangle gameBounds;
        private PaddleDirection paddleDirection = PaddleDirection.IDLE;

        private KeyboardState keyboardState;
        private KeyboardState prevKeyboardState;
        #endregion

        public Paddle(Player player, Rectangle gameBounds, Texture2D texture)
            : base(Vector2.Zero, texture)
        {
            this.player = player;
            this.gameBounds = gameBounds;
            this.initialLocation = new Vector2(gameBounds.Center.X - itemTexture.Width / 2, gameBounds.Bottom - 130);
            this.currentLocation = initialLocation;
        }

        /// <summary>
        /// Gets and sets the current paddle direction.
        /// </summary>
        public PaddleDirection PaddleDirection
        {
            get { return paddleDirection; }
            set { paddleDirection = value; }
        }

        /// <summary>
        /// Checks if the paddle is within the playable area of the screen.
        /// </summary>
        /// <returns>True if the player is inside the specified game are, false otherwise.</returns>
        private bool PlayerIsInBounds()
        {
            switch (paddleDirection)
            {
                case PaddleDirection.LEFT:
                    return gameBounds.Contains(CollisionRect.Left, CollisionRect.Y);

                case PaddleDirection.RIGHT:
                    return gameBounds.Contains(CollisionRect.Right + RIGHT_BOUND_MARGIN, CollisionRect.Y);

                default:
                    return false;
            }
        }

        /// <summary>
        /// Checks if the paddle is colliding with the walls, if that is the case, the paddle
        /// will not be able to move further in the current direction.
        /// </summary>
        private void CollisionCheck()
        {
            if (!PlayerIsInBounds())
            {
                switch (paddleDirection)
                {
                    case PaddleDirection.LEFT:
                        currentLocation.X = gameBounds.Left + LEFT_BOUND_MARGIN;
                        break;

                    case PaddleDirection.RIGHT:
                        currentLocation.X = gameBounds.Right - (itemTexture.Width + RIGHT_BOUND_MARGIN);
                        break;
                }
            }
        }

        /// <summary>
        /// Attempts to move the paddle to the left.
        /// </summary>
        private void MoveLeft(GameTime gameTime)
        {
            paddleDirection = PaddleDirection.LEFT;
            currentLocation.X -= MOVING_SPEED * (float)gameTime.ElapsedGameTime.TotalSeconds;
            CollisionCheck();
        }

        /// <summary>
        /// Attempts to move the paddle to the right.
        /// </summary>
        private void MoveRight(GameTime gameTime)
        {
            paddleDirection = PaddleDirection.RIGHT;
            currentLocation.X += MOVING_SPEED * (float)gameTime.ElapsedGameTime.TotalSeconds;
            CollisionCheck();
        }

        /// <summary>
        /// Checks if a keybord-key is down.
        /// </summary>
        /// <param name="key">The key to be checked.</param>
        /// <returns>True if that key is down, false otherwise.</returns>
        private bool IsKeyDown(Keys key)
        {
            return keyboardState.IsKeyDown(key);
        }

        /// <summary>
        /// Listens for user interaction with any supported physical game controls.
        /// </summary>
        private void HandleInput(GameTime gameTime)
        {
            if (!player.IsRemotePlayer)
            {
                keyboardState = Keyboard.GetState();

                if (IsKeyDown(Keys.Left))
                {
                    MoveLeft(gameTime);
                }
                else if (IsKeyDown(Keys.Right))
                {
                    MoveRight(gameTime);
                }
                else
                {
                    paddleDirection = PaddleDirection.IDLE;
                }
                prevKeyboardState = Keyboard.GetState();
            }

            else
            {
                switch (paddleDirection)
                {
                    case PaddleDirection.LEFT:
                        MoveLeft(gameTime);
                        break;

                    case PaddleDirection.RIGHT:
                        MoveRight(gameTime);
                        break;
                }
            }
        }

        /// <summary>
        /// Runs input handling and collision checks.
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            HandleInput(gameTime);
        }

        #region ISolidObject Members
        /// <summary>
        /// Gets the collision rect of the paddle.
        /// </summary>
        public Rectangle CollisionRect
        {
            get 
            {
                collisionRect.X = (int)currentLocation.X;
                collisionRect.Y = (int)currentLocation.Y;

                return collisionRect;
            }
            set
            {
                collisionRect = value;
            }
        }

        public bool OnMotion
        {
            get
            {
                return paddleDirection != PaddleDirection.IDLE;
            }
        }

        public void RegisterCollision()
        {
            SoundManager.PlayEffect(SoundEffects.PADDLE_HIT);
        }
        #endregion
    }
}